import { isPlainObject, deepMerge } from '../helpers/util'
import { AxiosRequestConfig } from '../types'

// 合并策略函数的 map
const strats = Object.create(null)

function defaultStrat(val1: any, val2: any): any {
  return typeof val2 !== 'undefined' ? val2 : val1
}

// 只取 config2 的策略函数
function fromVal2Strat(val1: any, val2: any): any {
  if (typeof val2 !== 'undefined') {
    return val2
  }
}

// 深度合并策略函数
function deepMergeStrat(val1: any, val2: any): any {
  if (isPlainObject(val2)) {
    // val2 为一个对象
    return deepMerge(val1, val2)
  } else if (typeof val2 !== 'undefined') {
    // val2 有值，但不是对象
    return val2
  } else if (isPlainObject(val1)) {
    // val2 没有值，且 val1 为对象
    return deepMerge(val2)
  } else if (typeof val1 !== 'undefined') {
    // val2 没有值，且 val1 有值，不为对象
    return val1
  }
}

// 向 strats 中根据不同的 key 设置不同的策略函数
const stratKeysFromVal2 = ['url', 'params', 'data']
stratKeysFromVal2.forEach(key => {
  strats[key] = fromVal2Strat
})

// 对 headers 字段应用深度合并策略
const stratKeysFromDeepMerge = ['headers', 'auth']
stratKeysFromDeepMerge.forEach(key => {
  strats[key] = deepMergeStrat
})

// 策略模式合并配置
export default function mergeConfig(
  config1: AxiosRequestConfig,
  config2?: AxiosRequestConfig
): AxiosRequestConfig {
  if (!config2) {
    config2 = {}
  }

  // 定义返回的 config
  const config = Object.create(null)

  // 按不同字段，有不同的合并策略
  for (let key in config2) {
    mergeField(key)
  }
  for (let key in config1) {
    if (!config2[key]) {
      mergeField(key)
    }
  }

  function mergeField(key: string): void {
    // 通过不同 key，获取不同的合并策略函数
    const strat = strats[key] || defaultStrat
    config[key] = strat(config1[key], config2![key])
  }

  return config
}
